home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / contrib / smaillog / smaillog.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-20  |  11.0 KB  |  506 lines

  1. /* @(#)contrib/smaillog/smaillog.c    1.7 9/20/92 12:54:34 */
  2. /*
  3.  *
  4.  * Parse smail3 log files.
  5.  *
  6.  * Copyright 1990, Jeff Beadles.  All rights reserved.
  7.  *
  8.  * Permission is granted to freely copy this program as long as it is
  9.  * not sold.  Source MUST be provided upon request.
  10.  *
  11.  * This message must be kept on all copies of the program.
  12.  *
  13.  *    jeff@onion.pdx.com
  14.  *  or  uunet!onion.pdx.com!jeff
  15.  *
  16.  * History: 
  17.  *    04.Apr.92
  18.  *    Jan-Piet Mens - <jpm@Logix.DE>
  19.  *    Added options -l to show size of message, and
  20.  *          option -d to output the date stamp additionally
  21.  *
  22.  *    $Header: smaillog.c,v 1.4 90/12/24 22:23:36 jeff Exp $
  23.  */
  24.  
  25.  
  26. #include <stdio.h>
  27. #include <ctype.h>
  28. #if defined(SYSV) || defined(POSIX)
  29. #include <string.h>
  30. #define index strchr
  31. #define rindex strrchr
  32. #else
  33. #include <strings.h>
  34. extern int strspn();
  35. #endif
  36.  
  37. #ifndef TRUE        
  38. # define TRUE        1
  39. # define FALSE        0
  40. #endif
  41.  
  42. void add_data();
  43. struct qlist *lookup();
  44. char *skipfields();
  45.  
  46. extern char *mspace();
  47. long int malloc_space = 0L;    /* Space malloc had to allocate */
  48.  
  49. /*
  50.  *    The qlist structure holds each unique "qid"'s entry.  It also
  51.  *    points to the data structures.
  52.  *    This is stored as a linked list with "qhead" pointing to the head
  53.  *    of the list.
  54.  */
  55. struct qlist {
  56.     char qid[32];
  57.     char date[24];
  58.     struct data *dp;
  59.     struct qlist *next;
  60.     };
  61.  
  62. /*
  63.  *    The data structure is the data information for the qlist structure.
  64.  *    There can be (and most likely are) multiple data structs for each
  65.  *    qlist structure.
  66.  *    tfl == "to from list"
  67.  */
  68.  
  69. struct data {
  70.     char *tfl;
  71.     struct data *next;
  72.     };
  73.  
  74. /*
  75.  *    The structures look like this:
  76.  *
  77.  *
  78.  *    (qhead)  qlist  -> data1 -> data2 -> data3
  79.  *          \|/
  80.  *         qlist  -> data1 -> data2
  81.  *          \|/
  82.  *         qlist  -> data1 -> data2 -> data3 -> data4 -> data5
  83.  *          \|/
  84.  *          NULL   (End of list )
  85.  *
  86.  */
  87.  
  88. static struct qlist *qhead;
  89. extern char *fgets();
  90.  
  91. /* ARGSUSED */
  92. int
  93. main(argc,argv)
  94. int argc;
  95. char **argv;
  96. {
  97.     struct data  *dtmp;
  98.     struct qlist *qtmp;
  99.  
  100.     char linebuf[4096], buffer[4096];
  101.     char line_date[24];
  102.     char *fg, *cp;
  103.     int lines = 0;
  104.     char to[4096], from[4096];
  105.     char *source, *dest;
  106.     char *ctmp;
  107.     int c, date_opt = FALSE, length_opt = FALSE;
  108.     extern int optind;
  109.     long msgbytes, atol();
  110.  
  111.     qhead =(struct qlist *)NULL;
  112.  
  113.     while ((c = getopt(argc, argv, "dl")) != EOF)
  114.         switch (c)
  115.         {
  116.             case 'd':
  117.                 date_opt = TRUE;
  118.                 break;
  119.             case 'l':
  120.                 length_opt = TRUE;
  121.                 break;
  122.             default:
  123.                 exit(fprintf(stderr, 
  124.                     "Usage: %s [-l] [-d] <input >output\n",
  125.                         *argv));
  126.         }
  127. /*
  128.  * Give a terse usage message if called with arguements.
  129.  */
  130.     if ( argc - optind != 0) {
  131.        fprintf(stderr, "Error:  Usage:  %s [-l] [-d] <input >output\n",argv[0]);
  132.        exit(1);
  133.     }
  134. /*
  135.  * Read each line from stdin
  136.  */
  137.     while ((fg=fgets(linebuf, sizeof(linebuf),stdin)) != (char *)NULL){
  138.         lines++;
  139.  
  140. /*
  141.  * Blast out the \n at the end... 
  142.  */
  143.         ctmp = rindex(fg, '\n');
  144.         if (ctmp) {
  145.             *ctmp = '\0';
  146.         }
  147.  
  148. /* 
  149.  * Skip "01/01/90 "  if date option not set. This is meant to run daily.
  150.  */
  151.         if (!date_opt)
  152.             if ((fg=skipfields(fg, 1)) == (char *)NULL) {
  153.                 printf("Error:  Skip line %d: %s\n",
  154.                        lines, linebuf);
  155.                 continue;
  156.             }
  157. /*
  158.  * Save the date, to be put in the qlist structure later
  159.  */
  160.  
  161.         (void)strncpy(line_date, fg, (date_opt) ? 17 : 8);
  162.  
  163. /* 
  164.  * Skip past the date field now, as we have it.
  165.  */
  166.  
  167.         if ((fg=skipfields(fg,(date_opt) ? 2 : 1)) == (char *)NULL) {
  168.             printf("Error:  Skip line %d: %s\n", linebuf);
  169.             continue;
  170.         }
  171. /*
  172.  * Catches "open_spool:" and "pid ##: smail daemon started" and other stuff.
  173.  * This just copies them verbatium to the output file.
  174.  * (At the head of the list)
  175.  */
  176.         if ( *fg != '[' ) {
  177.             printf("Warning: %s\n", fg);
  178.             continue;
  179.         }
  180. /* 
  181.  * Put qid in "buffer"  It looks like "[xxxxxx-xxxxxxx]".
  182.  * This is the identifier that is used to index into the qlist struct.
  183.  */
  184.         cp=buffer;
  185.         while(!isspace(*fg))
  186.             *cp++ = *fg++;
  187.         *cp = '\0';
  188.  
  189. /*
  190.  * buffer now contains the qid, so look it up in the qlist.
  191.  * Lookup will always return success.  It will malloc a new area if this
  192.  * is a new record.
  193.  */
  194.         qtmp = lookup(buffer);
  195.         (void)strcpy(qtmp->date, line_date);
  196.  
  197. /*
  198.  * Concatenate continuation lines to one single line.
  199.  */
  200.         fg = linebuf;
  201.         do {
  202.             if ((c = getc(stdin)) == EOF)
  203.                 break;
  204.             if (c != '|') {
  205.                 ungetc(c, stdin);
  206.                 break;
  207.             }
  208.             if (fgets(fg, sizeof(linebuf) - (fg - linebuf), stdin)
  209.                             == (char *)NULL)
  210.                 break;
  211.             ctmp = rindex(fg, '\n');
  212.             if (ctmp) {
  213.                 *ctmp = '\0';
  214.             }
  215.             fg += strlen(fg);
  216.         } while (sizeof(linebuf) > (fg - linebuf));
  217.         add_data(qtmp, linebuf);
  218.     }
  219. /*
  220.  * Note:  We are now out of the fgets() loop, and have all of the information
  221.  * loaded in the struct.  Time to print some stats, and process the info.
  222.  */
  223.  
  224.     printf("Processing %d lines.  (Used %ld bytes.)\n",lines,malloc_space);
  225.  
  226. /*
  227.  * Time to start stepping thru the list.  Start at the head, and process each
  228.  * qlist structure and it's data elements.
  229.  */
  230.     qtmp = qhead;
  231.  
  232.     while(qtmp) {
  233.         dtmp = qtmp->dp;
  234.         *from = *to = '\0';
  235.         while(dtmp) {
  236.  
  237. /* This marks a "from" line.  If so, then strip off the first 6 characters,
  238.  * and set the variable "dest" to point to it.  It will automatically
  239.  * append to the from line if needed.  It copies until it reaches the
  240.  * end of line, or a whitespace.
  241.  */
  242.             if ((fg=skipfields(dtmp->tfl, 1)) == (char *)NULL) {
  243.                 dtmp = dtmp->next;
  244.                 continue;
  245.             }
  246.             if ( !strncmp(fg, "from: ", 6)) {
  247.                 dest = from + strlen(from);
  248.                 source = fg + 6;
  249.                 while (*source && !isspace(*source) )
  250.                     *dest++ = *source++;
  251.                 *dest++ = ' ';
  252.                 *dest = '\0';
  253.  
  254.                 if (length_opt)
  255.                 {
  256.                     /*
  257.                      * Look for "size: " to get to the
  258.                      * bytes in the message. NOTE, the
  259.                      * size specified here, *excludes* the
  260.                      * headers that SMAIL adds.
  261.                      */
  262.  
  263.                     fg = source;
  264.                     while (fg = skipfields(fg, 1)) {
  265.                         if ( !strncmp(fg, "size: ", 6))
  266.                             break;
  267.                     }
  268.                     if (fg) {
  269.                         msgbytes = atol(fg + 6);
  270.                     }
  271.                 }
  272.                 dtmp = dtmp->next;
  273.                 continue;
  274.             }
  275. /* This is used to see if this is a "to" line.  If we find a "via" first,
  276.  * this is a remote address, so append it to the "to" variable with a
  277.  * terminating "!". The append the contents of the "to" line.
  278.  */
  279.             if ( !strncmp(fg, "via: ", 5)) {
  280.                 dest = to + strlen(to);
  281.                 source = fg + 5;
  282.                 while (*source && !isspace(*source) )
  283.                     *dest++ = *source++;
  284.                 if ((fg=skipfields(source, 1))
  285.                             == (char *)NULL) {
  286.                     *dest++ = ' ';
  287.                     *dest = '\0';
  288.                     dtmp = dtmp->next;
  289.                     continue;
  290.                 }
  291.                 *dest++ = '!';
  292.                 *dest = '\0';
  293.             }
  294.             if ( !strncmp(fg, "to: ", 4)) {
  295.                 dest = to + strlen(to);
  296.                 source = fg + 4;
  297.                 while (*source && !isspace(*source) )
  298.                     *dest++ = *source++;
  299.                 *dest++ = ' ';
  300.                 *dest = '\0';
  301.                 dtmp = dtmp->next;
  302.                 continue;
  303.             }
  304. /* A general error trap, that should not be reached, but you know how that
  305.  * goes... :-)
  306.  */
  307.             printf("What is %s?\n", dtmp->tfl);
  308.             dtmp = dtmp->next;
  309.         }
  310. /* At this point, we have traveled thru one entire message's data.
  311.  * Now, print the date, and the to and from information.  Try to figure
  312.  * out if this is to, or from someone on this machine, to do the "=>" "<="
  313.  * right.  If all else fails though, punt and guess.
  314.  */
  315.         if ( *to && *from) {
  316.             printf("%s: ", qtmp->date);
  317. /*
  318.  * kill trailing ',' and whitespace in the 'to' and 'from' fields
  319.  */
  320.             cp = to + strlen(to) - 1;
  321.             while ((*cp) && (isspace(*cp) || (*cp == ',')))
  322.                 *cp-- = '\0';
  323.  
  324.             cp = from + strlen(from) - 1;
  325.             while ((*cp) && (isspace(*cp) || (*cp == ',')))
  326.                 *cp-- = '\0';
  327. /*
  328.  * All of that for this to display it
  329.  */
  330.  
  331.             if (length_opt)
  332.                 printf("%6ld %s => %s\n", msgbytes, from, to);
  333.             else
  334.                 printf("%s => %s\n", from, to);
  335.         }
  336. /*
  337.  * Time to get the next element and process it.
  338.  */
  339.         qtmp = qtmp->next;
  340.     }
  341.  
  342.  
  343.     exit(0);
  344. }
  345.  
  346. /*
  347.  * This routine will add the data (str) to the qlist member "q"
  348.  */
  349.  
  350. void
  351. add_data(q, str)
  352. struct qlist *q;
  353. char *str;
  354. {
  355.     struct data *dtmp;
  356.     char *cp;
  357.  
  358. /*
  359.  * Save the string
  360.  */
  361.     cp = mspace( (unsigned int)(strlen(str)));
  362.     (void)strcpy(cp, str);
  363.  
  364. /*
  365.  * If the data area is empty, then just load this into it.
  366.  * and point the "next" entry to null.
  367.  */
  368.     if (q->dp == (struct data *)NULL) {
  369.         q->dp = (struct data *)mspace(sizeof(struct data));
  370.         dtmp = q->dp;
  371.         dtmp->tfl = cp;
  372.         dtmp->next = (struct data *)NULL;
  373.         return;
  374.     }
  375. /*
  376.  * otherwise, search and find the end of the data list for this qlist.
  377.  * Then, add it to the end.
  378.  */
  379.     dtmp = q->dp;
  380.     while(dtmp->next != (struct data *)NULL) {
  381.         dtmp= dtmp->next;
  382.     }
  383. /*
  384.  * Get space for the new element, and add it to the end of the list.
  385.  * Then, move to the new element, and add the data to it.
  386.  * After that, set the "next" flag to null, to show that this is now
  387.  * the end of the list.
  388.  */
  389.     dtmp->next=(struct data *)mspace(sizeof(struct data));
  390.     dtmp = dtmp->next;
  391.     dtmp->tfl = cp;
  392.     dtmp->next = (struct data *)NULL;
  393. }
  394.  
  395.  
  396. /*
  397.  * This routine will search the qlist for an element with the "key" of
  398.  * the qid this is called with.  If not found, then it will automatically
  399.  * allocate a new member, and initialize it properly
  400.  */
  401. struct qlist *
  402. lookup(str)
  403. char *str;
  404. {
  405.  
  406.     struct qlist *qtmp = qhead;
  407.  
  408.  
  409. /*
  410.  * Is this an empty list?  If so, then add to head.
  411.  */
  412.     if ( qhead == (struct qlist *)NULL) {
  413.         qhead = (struct qlist *)mspace(sizeof(struct qlist));
  414.         qtmp = qhead;
  415.         (void)strcpy(qtmp->qid, str);
  416.         qtmp->dp = (struct data *)NULL;
  417.         qtmp->next = (struct qlist *) NULL;
  418.         return (qtmp);
  419.     }
  420.     while (1) {
  421. /*
  422.  * Does this one match?  If so, then return it.
  423.  */
  424.         if (!strcmp(qtmp->qid, str)) {
  425.             return(qtmp);
  426.         }
  427. /*
  428.  * If at the end of the list, then add it.
  429.  */
  430.         if (qtmp->next == (struct qlist *)NULL) {
  431.             qtmp->next=(struct qlist *)mspace(sizeof(struct qlist));
  432.             qtmp = qtmp->next;
  433.             (void)strcpy(qtmp->qid, str);
  434.             qtmp->dp = (struct data *)NULL;
  435.             qtmp->next = (struct qlist *) NULL;
  436.             return (qtmp);
  437.         }
  438.         qtmp = qtmp->next;
  439.     }
  440. }
  441.  
  442.  
  443. /*
  444.  * This routine just skips past whitespace-delimited fields in a string.
  445.  * IE:  If called as skipfields("a bb ccc dddd eeeee",2) it will return
  446.  *  "ccc dddd eeeee"
  447.  */
  448. char *
  449. skipfields(str,count)
  450. char *str;
  451. int count;
  452.  
  453. {
  454.     if ( count < 1 )
  455.         return( (char *)NULL);
  456.  
  457.     while (count--) {
  458.         while((*str) && !(isspace(*str)))
  459.             str++;
  460.         if (!*str)
  461.             return( (char *)NULL);
  462.         while((*str) && (isspace(*str)))
  463.             str++;
  464.         if (!*str)
  465.             return( (char *)NULL);
  466.     }
  467.     return(str);
  468. }
  469.  
  470. /* This routine is a general "malloc" routine.  It does two additional
  471.  * functions though.  One, is to keep track of how much space has been
  472.  * malloc'ed, and the second is to automatically check for errors, and bail-
  473.  * out if needed.  This prevents me from having to put the same code around
  474.  * all of the malloc's.
  475.  *
  476.  * It also allocates "FUZZ" more bytes than were requested.  If you're 
  477.  * having troubles with this, then change it to something >0.
  478.  * (Don't make it less than 0 though, snicker :-)
  479.  */
  480.  
  481. #ifndef FUZZ
  482. #define FUZZ sizeof(short)
  483. #endif /* !FUZZ */
  484.  
  485. char *
  486. mspace(len)
  487. unsigned int len;
  488. {
  489.     static char *mp;
  490.     extern char *malloc();
  491.  
  492.     len += FUZZ;
  493.  
  494.     mp = malloc(len);
  495.  
  496.     if (mp) { 
  497.       malloc_space += len;
  498.       return(mp);
  499.     } else {
  500.       fprintf(stderr, "Malloc:\tOut of space (Requested %d bytes)\n",len);
  501.       fprintf(stderr, "\tAlready requested %ld bytes\n", malloc_space);
  502.       exit(1);
  503.     }
  504.     /*NOTREACHED*/
  505. }
  506.